package com.brycen.nio.chat;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;

/**
 * 结合buffer、channel、selector做的综合应用
 */
public class Server {
    private ServerSocketChannel ssChannel;
    private Selector selector;
    private int PORT=9999;

    public Server(){
        try {
            //创建服务端通道
            ssChannel = ServerSocketChannel.open();
            //监听端口
            ssChannel.bind(new InetSocketAddress(PORT));
            //非阻塞通道
            ssChannel.configureBlocking(false);
            //创建选择器
            selector = Selector.open();
            //将通道注册到选择器上
            ssChannel.register(selector, SelectionKey.OP_ACCEPT);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void listen(){
        try {
            //循环获取准备好的事件，如果没有该方法会阻塞
            while (selector.select()>0){
                //获取所有已经准备好的事件
                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                while (iterator.hasNext()){
                    SelectionKey sk = iterator.next();
                    //判断事件是否为注册事件
                    if (sk.isAcceptable()){
                        //获取注册事件对应的通道
                        SocketChannel channel = ssChannel.accept();
                        //设置客户端连接的通道为非阻塞模式
                        channel.configureBlocking(false);
                        //将客户端通道注册到选择器上
                        channel.register(selector,SelectionKey.OP_READ);
                    } else if (sk.isReadable()) {
                        //读事件处理
                        readClientData(sk);
                    }
                    //事件处理完成，移除该事件
                    iterator.remove();
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    private void readClientData(SelectionKey sk) {
        try {
            //获取事件对应的通道
            SocketChannel socketChannel = (SocketChannel) sk.channel();
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
            int len;
            //读取数据
            while ((len=socketChannel.read(byteBuffer))>0){
                String data = new String(byteBuffer.array(),0,byteBuffer.remaining());
                //将数据发送给其他客户端
                sendMsgToOtherClient(data,socketChannel);
            }
            //如果上-1则表示客户端已经断开连接
            if (len==-1){
                //关闭通道，从选择器上卸载该通道
                closeClient(sk);
            }
        } catch (IOException e) {
            try {
                //发送异常，关闭通道，从选择器上卸载该通道
                closeClient(sk);
            } catch (IOException ex) {
                ex.printStackTrace();
            }
            e.printStackTrace();
        }
    }

    private void sendMsgToOtherClient(String data, SocketChannel socketChannel) {
        try{
            //获取注册在选择器上的所有通道
            Iterator<SelectionKey> iterator = selector.keys().iterator();
            while (iterator.hasNext()){
                //获取事件对应的通道
                Channel channel = iterator.next().channel();
                //判断通道为客户端通道并且不能是发送端自己
                if(channel instanceof SocketChannel && channel!=socketChannel){
                    //将字符串包装为ByteBuffer缓冲区
                    ByteBuffer byteBuffer = ByteBuffer.wrap(data.getBytes());
                    //发送数据给其他客户端
                    ((SocketChannel)channel).write(byteBuffer);
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    private void closeClient(SelectionKey sk) throws IOException {
        //将该事件从选择器上取消
        sk.cancel();
        //关闭通道
        sk.channel().close();
    }

    public static void main(String[] args) {
        Server server = new Server();
        server.listen();
    }
}
